package com.runvp.netty.constant;

import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * netty常量key
 */
@Slf4j
public class NettyConstant {

    /**
     * 公用客户端key
     */
    public static String CLIENT_CHANNEL = "client_channel";
    /**
     * 管理全局的channel
     * GlobalEventExecutor.INSTANCE  全局事件监听器
     * 一旦将channel 加入 ChannelGroup 就不要用手动去
     * 管理channel的连接失效后移除操作，他会自己移除
     */
    public static volatile ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    public static volatile Map<String,Channel> channelIdMap = new ConcurrentHashMap<>();

    /**
     * 为了实现私聊功能，这里key存储用户的唯一标识，
     * 我保存 客户端的端口号
     * 当然 这个集合也需要自己去维护 用户的上下线 不能像 ChannelGroup那样自己去维护
     */
    public static volatile Map<String,Channel> all = new ConcurrentHashMap<>();
    /**
     * 在线的设备id集合
     */
    public static volatile Map<String,String> allOnlineChannleIds =  new ConcurrentHashMap<>();

    /**
     * 移除通道
     * @param channel
     */
    public static void removeChannel(Channel channel){
        String channelId = getChannelId(channel);
        String sn = allOnlineChannleIds.get(channelId);
        //清理无效通道
        if(StringUtils.isNotBlank(sn) && sn.length() == 15){
            log.info("SN【{}】合法通道,跳过",sn);
            return;
        }
        all.remove(sn);
        clearChannel(channel);
        log.info("设备【{}】通道移除成功",sn);
        log.warn("清理不合法SN【{}】以及通道【{}】",sn,channelId);
    }


    /**
     * 清理通道并关闭
     * @param channel
     */
    private static void clearChannel(Channel channel){
        if(null == channel){
            return;
        }
        String channelId = getChannelId(channel);
        try{
            channelIdMap.remove(channelId);
            channels.remove(channel);
            allOnlineChannleIds.remove(channelId);
            log.info("清理不合法通道【{}】",channelId);
        }catch (Exception e){
            log.error("清理不合法通道【{}】异常",channelId,e);
        }
    }
    /**
     * 默认使用短ID
     * @param channel
     * @return str
     */
    public static String getChannelId(Channel channel){
        if(null == channel){
            return "";
        }
        return channel.id().asShortText();
    }

    /**
     * 建立连接以后第一个调用的添加通道方法
     * @param channel
     */
    public static void channelInitAdd(Channel channel){
        String channelId = getChannelId(channel);
        channels.add(channel);
        channelIdMap.put(channelId,channel);
    }


    /**
     * 清理错误通道
     */
    public synchronized static void removeErrorChannel(){
        try {
            log.info("延迟3秒钟开始处理通道");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Map<String,String> normals = getNormals();
        log.info("正常通道数:{}",normals.size());
        int num = clear(normals);
        log.info("本次清理无效通道数【{}】",num);
    }

    /**
     * 清理失效通道
     * @param normals
     */
    private static int clear(Map<String,String> normals) {
        allOnlineChannleIds.entrySet().removeIf(map -> !normals.containsKey(map.getKey()));
        all.entrySet().removeIf(map -> !normals.containsValue(map.getKey()));
        Iterator<Map.Entry<String, Channel>> it = channelIdMap.entrySet().iterator();
        int num = 0;
        while(it.hasNext()){
            Map.Entry<String, Channel> channelEntry = it.next();
            if(!normals.containsKey(channelEntry.getKey())){
                it.remove();
                channels.remove(channelEntry.getValue());
                num++;
            }
        }
        return num;
    }

    /**
     * 获取设备ip和端口
     * @param channel
     * @return String
     */
    public static String getClientIpPort(Channel channel){
        InetSocketAddress inSocket = (InetSocketAddress) channel.remoteAddress();
        String ip = inSocket.getAddress().getHostAddress();
        String port = String.valueOf(inSocket.getPort());
        return ip+":"+port;
    }
    /**
     * 获取正常通道
     * @return list
     */
    public static Map<String,String> getNormals(){
        Map<String,String> normals = new HashMap<>();
        for(Map.Entry<String,String> entry:allOnlineChannleIds.entrySet()) {
            String cid = entry.getKey();
            Channel channel = channelIdMap.get(cid);
            if(null == channel){
                continue;
            }
            String sn = entry.getValue();
            if(channel.isActive() && !normals.containsValue(sn)){
                normals.put(cid,sn);
            }
        }
        return normals;
    }
    /**
     * 添加通道到集合
     * @param sn
     * @param channel
     */
    public static void addChannel(String sn,Channel channel){
        String channelId = getChannelId(channel);
        if(StringUtils.isBlank(sn)){
            log.error("设备SN为空，不执行添加绑定【{}】",channelId);
            return;
        }
        allOnlineChannleIds.put(channelId,sn);
        all.put(sn, channel);
        channelInitAdd(channel);
        log.info("设备SN【{}】与channelID【{}】绑定成功",sn,channelId);
    }

    public static void main(String[] args) {
        System.out.println("6000000038".substring(2,6));
        System.out.println("6038002101".substring(2,6));
    }
}
